home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / CGIHTTPServer.py < prev    next >
Text File  |  2009-11-02  |  13KB  |  367 lines

  1. """CGI-savvy HTTP Server.
  2.  
  3. This module builds on SimpleHTTPServer by implementing GET and POST
  4. requests to cgi-bin scripts.
  5.  
  6. If the os.fork() function is not present (e.g. on Windows),
  7. os.popen2() is used as a fallback, with slightly altered semantics; if
  8. that function is not present either (e.g. on Macintosh), only Python
  9. scripts are supported, and they are executed by the current process.
  10.  
  11. In all cases, the implementation is intentionally naive -- all
  12. requests are executed sychronously.
  13.  
  14. SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
  15. -- it may execute arbitrary Python code or external programs.
  16.  
  17. Note that status code 200 is sent prior to execution of a CGI script, so
  18. scripts cannot send other status codes such as 302 (redirect).
  19. """
  20.  
  21.  
  22. __version__ = "0.4"
  23.  
  24. __all__ = ["CGIHTTPRequestHandler"]
  25.  
  26. import os
  27. import sys
  28. import urllib
  29. import BaseHTTPServer
  30. import SimpleHTTPServer
  31. import select
  32.  
  33.  
  34. class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  35.  
  36.     """Complete HTTP server with GET, HEAD and POST commands.
  37.  
  38.     GET and HEAD also support running CGI scripts.
  39.  
  40.     The POST command is *only* implemented for CGI scripts.
  41.  
  42.     """
  43.  
  44.     # Determine platform specifics
  45.     have_fork = hasattr(os, 'fork')
  46.     have_popen2 = hasattr(os, 'popen2')
  47.     have_popen3 = hasattr(os, 'popen3')
  48.  
  49.     # Make rfile unbuffered -- we need to read one line and then pass
  50.     # the rest to a subprocess, so we can't use buffered input.
  51.     rbufsize = 0
  52.  
  53.     def do_POST(self):
  54.         """Serve a POST request.
  55.  
  56.         This is only implemented for CGI scripts.
  57.  
  58.         """
  59.  
  60.         if self.is_cgi():
  61.             self.run_cgi()
  62.         else:
  63.             self.send_error(501, "Can only POST to CGI scripts")
  64.  
  65.     def send_head(self):
  66.         """Version of send_head that support CGI scripts"""
  67.         if self.is_cgi():
  68.             return self.run_cgi()
  69.         else:
  70.             return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
  71.  
  72.     def is_cgi(self):
  73.         """Test whether self.path corresponds to a CGI script,
  74.         and return a boolean.
  75.  
  76.         This function sets self.cgi_info to a tuple (dir, rest)
  77.         when it returns True, where dir is the directory part before
  78.         the CGI script name.  Note that rest begins with a
  79.         slash if it is not empty.
  80.  
  81.         The default implementation tests whether the path
  82.         begins with one of the strings in the list
  83.         self.cgi_directories (and the next character is a '/'
  84.         or the end of the string).
  85.         """
  86.  
  87.         path = self.path
  88.  
  89.         for x in self.cgi_directories:
  90.             i = len(x)
  91.             if path[:i] == x and (not path[i:] or path[i] == '/'):
  92.                 self.cgi_info = path[:i], path[i+1:]
  93.                 return True
  94.         return False
  95.  
  96.     cgi_directories = ['/cgi-bin', '/htbin']
  97.  
  98.     def is_executable(self, path):
  99.         """Test whether argument path is an executable file."""
  100.         return executable(path)
  101.  
  102.     def is_python(self, path):
  103.         """Test whether argument path is a Python script."""
  104.         head, tail = os.path.splitext(path)
  105.         return tail.lower() in (".py", ".pyw")
  106.  
  107.     def run_cgi(self):
  108.         """Execute a CGI script."""
  109.         path = self.path
  110.         dir, rest = self.cgi_info
  111.  
  112.         i = path.find('/', len(dir) + 1)
  113.         while i >= 0:
  114.             nextdir = path[:i]
  115.             nextrest = path[i+1:]
  116.  
  117.             scriptdir = self.translate_path(nextdir)
  118.             if os.path.isdir(scriptdir):
  119.                 dir, rest = nextdir, nextrest
  120.                 i = path.find('/', len(dir) + 1)
  121.             else:
  122.                 break
  123.  
  124.         # find an explicit query string, if present.
  125.         i = rest.rfind('?')
  126.         if i >= 0:
  127.             rest, query = rest[:i], rest[i+1:]
  128.         else:
  129.             query = ''
  130.  
  131.         # dissect the part after the directory name into a script name &
  132.         # a possible additional path, to be stored in PATH_INFO.
  133.         i = rest.find('/')
  134.         if i >= 0:
  135.             script, rest = rest[:i], rest[i:]
  136.         else:
  137.             script, rest = rest, ''
  138.  
  139.         scriptname = dir + '/' + script
  140.         scriptfile = self.translate_path(scriptname)
  141.         if not os.path.exists(scriptfile):
  142.             self.send_error(404, "No such CGI script (%r)" % scriptname)
  143.             return
  144.         if not os.path.isfile(scriptfile):
  145.             self.send_error(403, "CGI script is not a plain file (%r)" %
  146.                             scriptname)
  147.             return
  148.         ispy = self.is_python(scriptname)
  149.         if not ispy:
  150.             if not (self.have_fork or self.have_popen2 or self.have_popen3):
  151.                 self.send_error(403, "CGI script is not a Python script (%r)" %
  152.                                 scriptname)
  153.                 return
  154.             if not self.is_executable(scriptfile):
  155.                 self.send_error(403, "CGI script is not executable (%r)" %
  156.                                 scriptname)
  157.                 return
  158.  
  159.         # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
  160.         # XXX Much of the following could be prepared ahead of time!
  161.         env = {}
  162.         env['SERVER_SOFTWARE'] = self.version_string()
  163.         env['SERVER_NAME'] = self.server.server_name
  164.         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
  165.         env['SERVER_PROTOCOL'] = self.protocol_version
  166.         env['SERVER_PORT'] = str(self.server.server_port)
  167.         env['REQUEST_METHOD'] = self.command
  168.         uqrest = urllib.unquote(rest)
  169.         env['PATH_INFO'] = uqrest
  170.         env['PATH_TRANSLATED'] = self.translate_path(uqrest)
  171.         env['SCRIPT_NAME'] = scriptname
  172.         if query:
  173.             env['QUERY_STRING'] = query
  174.         host = self.address_string()
  175.         if host != self.client_address[0]:
  176.             env['REMOTE_HOST'] = host
  177.         env['REMOTE_ADDR'] = self.client_address[0]
  178.         authorization = self.headers.getheader("authorization")
  179.         if authorization:
  180.             authorization = authorization.split()
  181.             if len(authorization) == 2:
  182.                 import base64, binascii
  183.                 env['AUTH_TYPE'] = authorization[0]
  184.                 if authorization[0].lower() == "basic":
  185.                     try:
  186.                         authorization = base64.decodestring(authorization[1])
  187.                     except binascii.Error:
  188.                         pass
  189.                     else:
  190.                         authorization = authorization.split(':')
  191.                         if len(authorization) == 2:
  192.                             env['REMOTE_USER'] = authorization[0]
  193.         # XXX REMOTE_IDENT
  194.         if self.headers.typeheader is None:
  195.             env['CONTENT_TYPE'] = self.headers.type
  196.         else:
  197.             env['CONTENT_TYPE'] = self.headers.typeheader
  198.         length = self.headers.getheader('content-length')
  199.         if length:
  200.             env['CONTENT_LENGTH'] = length
  201.         referer = self.headers.getheader('referer')
  202.         if referer:
  203.             env['HTTP_REFERER'] = referer
  204.         accept = []
  205.         for line in self.headers.getallmatchingheaders('accept'):
  206.             if line[:1] in "\t\n\r ":
  207.                 accept.append(line.strip())
  208.             else:
  209.                 accept = accept + line[7:].split(',')
  210.         env['HTTP_ACCEPT'] = ','.join(accept)
  211.         ua = self.headers.getheader('user-agent')
  212.         if ua:
  213.             env['HTTP_USER_AGENT'] = ua
  214.         co = filter(None, self.headers.getheaders('cookie'))
  215.         if co:
  216.             env['HTTP_COOKIE'] = ', '.join(co)
  217.         # XXX Other HTTP_* headers
  218.         # Since we're setting the env in the parent, provide empty
  219.         # values to override previously set values
  220.         for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
  221.                   'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
  222.             env.setdefault(k, "")
  223.         os.environ.update(env)
  224.  
  225.         self.send_response(200, "Script output follows")
  226.  
  227.         decoded_query = query.replace('+', ' ')
  228.  
  229.         if self.have_fork:
  230.             # Unix -- fork as we should
  231.             args = [script]
  232.             if '=' not in decoded_query:
  233.                 args.append(decoded_query)
  234.             nobody = nobody_uid()
  235.             self.wfile.flush() # Always flush before forking
  236.             pid = os.fork()
  237.             if pid != 0:
  238.                 # Parent
  239.                 pid, sts = os.waitpid(pid, 0)
  240.                 # throw away additional data [see bug #427345]
  241.                 while select.select([self.rfile], [], [], 0)[0]:
  242.                     if not self.rfile.read(1):
  243.                         break
  244.                 if sts:
  245.                     self.log_error("CGI script exit status %#x", sts)
  246.                 return
  247.             # Child
  248.             try:
  249.                 try:
  250.                     os.setuid(nobody)
  251.                 except os.error:
  252.                     pass
  253.                 os.dup2(self.rfile.fileno(), 0)
  254.                 os.dup2(self.wfile.fileno(), 1)
  255.                 os.execve(scriptfile, args, os.environ)
  256.             except:
  257.                 self.server.handle_error(self.request, self.client_address)
  258.                 os._exit(127)
  259.  
  260.         elif self.have_popen2 or self.have_popen3:
  261.             # Windows -- use popen2 or popen3 to create a subprocess
  262.             import shutil
  263.             if self.have_popen3:
  264.                 popenx = os.popen3
  265.             else:
  266.                 popenx = os.popen2
  267.             cmdline = scriptfile
  268.             if self.is_python(scriptfile):
  269.                 interp = sys.executable
  270.                 if interp.lower().endswith("w.exe"):
  271.                     # On Windows, use python.exe, not pythonw.exe
  272.                     interp = interp[:-5] + interp[-4:]
  273.                 cmdline = "%s -u %s" % (interp, cmdline)
  274.             if '=' not in query and '"' not in query:
  275.                 cmdline = '%s "%s"' % (cmdline, query)
  276.             self.log_message("command: %s", cmdline)
  277.             try:
  278.                 nbytes = int(length)
  279.             except (TypeError, ValueError):
  280.                 nbytes = 0
  281.             files = popenx(cmdline, 'b')
  282.             fi = files[0]
  283.             fo = files[1]
  284.             if self.have_popen3:
  285.                 fe = files[2]
  286.             if self.command.lower() == "post" and nbytes > 0:
  287.                 data = self.rfile.read(nbytes)
  288.                 fi.write(data)
  289.             # throw away additional data [see bug #427345]
  290.             while select.select([self.rfile._sock], [], [], 0)[0]:
  291.                 if not self.rfile._sock.recv(1):
  292.                     break
  293.             fi.close()
  294.             shutil.copyfileobj(fo, self.wfile)
  295.             if self.have_popen3:
  296.                 errors = fe.read()
  297.                 fe.close()
  298.                 if errors:
  299.                     self.log_error('%s', errors)
  300.             sts = fo.close()
  301.             if sts:
  302.                 self.log_error("CGI script exit status %#x", sts)
  303.             else:
  304.                 self.log_message("CGI script exited OK")
  305.  
  306.         else:
  307.             # Other O.S. -- execute script in this process
  308.             save_argv = sys.argv
  309.             save_stdin = sys.stdin
  310.             save_stdout = sys.stdout
  311.             save_stderr = sys.stderr
  312.             try:
  313.                 save_cwd = os.getcwd()
  314.                 try:
  315.                     sys.argv = [scriptfile]
  316.                     if '=' not in decoded_query:
  317.                         sys.argv.append(decoded_query)
  318.                     sys.stdout = self.wfile
  319.                     sys.stdin = self.rfile
  320.                     execfile(scriptfile, {"__name__": "__main__"})
  321.                 finally:
  322.                     sys.argv = save_argv
  323.                     sys.stdin = save_stdin
  324.                     sys.stdout = save_stdout
  325.                     sys.stderr = save_stderr
  326.                     os.chdir(save_cwd)
  327.             except SystemExit, sts:
  328.                 self.log_error("CGI script exit status %s", str(sts))
  329.             else:
  330.                 self.log_message("CGI script exited OK")
  331.  
  332.  
  333. nobody = None
  334.  
  335. def nobody_uid():
  336.     """Internal routine to get nobody's uid"""
  337.     global nobody
  338.     if nobody:
  339.         return nobody
  340.     try:
  341.         import pwd
  342.     except ImportError:
  343.         return -1
  344.     try:
  345.         nobody = pwd.getpwnam('nobody')[2]
  346.     except KeyError:
  347.         nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
  348.     return nobody
  349.  
  350.  
  351. def executable(path):
  352.     """Test for executable file."""
  353.     try:
  354.         st = os.stat(path)
  355.     except os.error:
  356.         return False
  357.     return st.st_mode & 0111 != 0
  358.  
  359.  
  360. def test(HandlerClass = CGIHTTPRequestHandler,
  361.          ServerClass = BaseHTTPServer.HTTPServer):
  362.     SimpleHTTPServer.test(HandlerClass, ServerClass)
  363.  
  364.  
  365. if __name__ == '__main__':
  366.     test()
  367.